package net.starwifi.duobao.service.impl;

import com.google.common.collect.Maps;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.starwifi.duobao.dto.DBTimesStockResult;
import net.starwifi.duobao.entity.*;
import net.starwifi.duobao.mvc.Page;
import net.starwifi.duobao.mvc.PageView;
import net.starwifi.duobao.repository.DBTimesRepository;
import net.starwifi.duobao.service.*;
import net.starwifi.duobao.utils.CompressUtils;
import net.starwifi.duobao.utils.DBTimesHelper;
import net.starwifi.duobao.utils.SmsUtils;
import net.starwifi.duobao.utils.sms.SMSUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Administrator on 2015/11/19.
 */
@Service
public class DBTimesServiceImpl extends BaseServiceImpl<DBTimes, Long> implements DBTimesService {

    @Autowired
    DBTimesRepository repository;

    @Autowired
    DBProductService dbProductService;

    @Resource(name = "taskExecutor")
    private TaskExecutor taskExecutor;

    @Autowired
    DBOrderService dbOrderService;

    @Autowired
    JedisPool jedisPool;

    @Autowired
    DBCodeService dbCodeService;

    @Autowired
    StaticService staticService;

    @Resource(name = "ehCacheManager")
    private CacheManager cacheManager;

    @Autowired
    SettingService settingService;


    @Transactional
    public void updateWantOrder(Long timesId, Long orderId) {
        repository.updateWantOrder(timesId, orderId + "");
    }

    @Override
    public DBTimes create(DBTimes dbTimes) {
        save(dbTimes);
        flush();
        createTimesPool(dbTimes.getTimesNo(), dbTimes.getQuantity());
        return dbTimes;
    }

    public DBTimes findByTimesNo(String timeNo) {
        return repository.findByTimesNo(timeNo);
    }


    /**
     * 生成夺宝ID.
     */
    public synchronized Long getDBID() {
        DateFormat format = new SimpleDateFormat("yyMMddHHmmss");
        String s = format.format(System.currentTimeMillis());
        return Long.parseLong(s);
    }

    /**
     * 系统开奖通知短信
     * @param q
     * @return
     */
//    public void sendTipOperatorSMS(DBTimes dbTimes) {
//        try {
//            String phoneConfig = settingService.findByCode("dbTimesLotteryTip").getConfigValue();
//            String[] phoneArray = phoneConfig.split(",");
//            for (String mobile : phoneArray) {
//                List<String> dataList = new ArrayList<String>();
//                String content = "当前开奖商品" + dbTimes.getDbProduct().getName() + ",即将进行第" + dbTimes.getTimesNo() + "期开奖";
//                dataList.add(content);
//                dataList.add("http://dwz.cn/2RqTMs");
//                SMSUtils.send(mobile, dataList, "10016");
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }


    @Override
    @SuppressWarnings("unchecked")
    public Map<Long, Integer> getCQS(String q) {
        String[] ids = q.split("_");
        final Map<Long, Integer> data = new HashMap<Long, Integer>();
        Ehcache cache = cacheManager.getEhcache(DBTimes.CURRENT_QUANTITY_CACHE_NAME);
        for (String id : ids) {
            Element element = cache.get(id);
            Integer cq;
            if (element != null) {
                cq = (Integer) element.getObjectValue();
            } else {
                DBTimes dbTimes = find(Long.parseLong(id));
                if (dbTimes == null) {
                    cq = 0;
                } else {
                    cq = dbTimes.getCurrentQuantity();
                    cache.put(new Element(id, cq));
                }
            }
            data.put(Long.parseLong(id), cq);
        }
        return data;
    }

    /**
     * 获取进度缓存
     */
    @SuppressWarnings("unchecked")
    public Integer getCQ(String id) {
        Ehcache cache = cacheManager.getEhcache(DBTimes.CURRENT_QUANTITY_CACHE_NAME);
        Element element = cache.get(id);
        Integer cq;
        if (element != null) {
            cq = (Integer) element.getObjectValue();
        } else {
            DBTimes dbTimes = find(Long.parseLong(id));
            if (dbTimes == null) {
                return 0;
            }
            cq = dbTimes.getCurrentQuantity();
            cache.put(new Element(id, cq));
        }
        return cq;
    }


    /**
     * 更新点击数
     *
     * @param id    夺宝期ID
     * @param count 新增数量
     */
    @SuppressWarnings("unchecked")
    private void updateCQ(String id, Integer count) {
        Ehcache cache = cacheManager.getEhcache(DBTimes.CURRENT_QUANTITY_CACHE_NAME);
        Element element = cache.get(id);
        Integer cq = 0;
        if (element != null) {
            cq = (Integer) element.getObjectValue();
        } else {
            DBTimes dbTimes = find(Long.parseLong(id));
            if (dbTimes != null) {
                cq = dbTimes.getCurrentQuantity();
            }
        }
        cq = cq + count;
        cache.put(new Element(id, cq));
    }


    /**
     * 分配库存
     */
    @Override
    @Transactional
    public DBTimesStockResult lockStock(DBOrder dbOrder) {
        DBTimes dbTimes = find(dbOrder.getDbTimes().getId());
        if (dbTimes.getIsOver()) {
            return new DBTimesStockResult(dbOrder.getQuantity(), 0, false);
        }
        if (dbTimes.getIsLottery()) {
            return new DBTimesStockResult(dbOrder.getQuantity(), 0, false);
        }
        if (dbOrder.getQuantity() == 0) {
            return new DBTimesStockResult(0, 0, false);
        }
        int hasSum = dbTimes.getQuantity() - dbTimes.getCurrentQuantity();//获取剩余库存(数据库)
        if (hasSum <= 0) {
            return new DBTimesStockResult(dbOrder.getQuantity(), 0, false); //库存已为空
        }
        DBTimesStockResult result = new DBTimesStockResult();
        result.setCount(dbOrder.getQuantity());
        if (hasSum > dbOrder.getQuantity()) { //库存足够
            result.setAllowCount(dbOrder.getQuantity());
            result.setIsTimesOver(false);
        } else {
            result.setAllowCount(hasSum);
            result.setIsTimesOver(true);
        }
        List<String> codeList = getTimesCodeList(dbOrder.getTimesNo(), result.getAllowCount());//分配夺宝码
        String codeInfo = "";
        for (String code : codeList) {
            DBCode dbCode = new DBCode();
            dbCode.setTimesNo(dbOrder.getTimesNo());
            dbCode.setMemberId(dbOrder.getMemberId());
            dbCode.setDbOrder(dbOrder);
            Integer lastCode = 10000000 + Integer.parseInt(code);
            dbCode.setDbCode(lastCode);
            codeInfo = codeInfo + "" + lastCode + ",";
            dbCodeService.save(dbCode);
        }
        //获取误差比例
        if (codeList.size() < result.getAllowCount()) {
            int lSize = result.getAllowCount() - codeList.size();//误差数量
            result.setAllowCount(codeList.size());//夺宝码库存已经不正常
            if (result.isTimesOver()) { //如果正好应该开奖了
                try {
                    float re = (float) lSize / dbTimes.getQuantity();
                    if (re > 0.02) {
                        result.setIsTimesOver(Boolean.FALSE);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        dbTimes.setCurrentQuantity(dbTimes.getCurrentQuantity() + result.getAllowCount());
        updateCQ(dbTimes.getId().toString(), result.getAllowCount()); //更新缓存
        result.setCodeInfo(codeInfo);
        update(dbTimes); //更新当前库存信息
        return result;
    }

    /**
     * 生成幸运号码
     */
    @Transactional
    public void findLottery(DBTimes dbTimes) {
        dbTimes.setIsOver(Boolean.TRUE); //将商品改为下架
        DBProduct dbProduct = dbTimes.getDbProduct();
        dbProduct.setIsMarketable(Boolean.FALSE);
        dbProduct.setDbTimes(null);
        dbProductService.update(dbProduct);//下架改商品
        try {
            //获取参考数值A
            Integer codeA = Integer.parseInt(DBTimesHelper.getSH());
            dbTimes.setEndDate(new Date());
            List<net.starwifi.duobao.mvc.Order> orderBy = new ArrayList<net.starwifi.duobao.mvc.Order>();
            orderBy.add(net.starwifi.duobao.mvc.Order.desc("id"));
            List<DBOrder> orderList = dbOrderService.getListData(0, 20, "o.timesNo=?1 and o.paymentStatus=?2", new Object[]{dbTimes.getTimesNo(), 1}, orderBy);
            //获取参考数值B
            Long codeB = 0L;
            for (DBOrder order : orderList) {
                SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
                Long d = Long.parseLong(sdf.format(order.getCreateDate()));
                codeB = codeB + d;
            }
            Long resultCode = 10000000 + (codeA + codeB) % dbTimes.getQuantity(); //幸运号码
            DBCode dbCode = dbCodeService.findByTimesNoAndDbCode(dbTimes.getTimesNo(), Integer.valueOf(resultCode + ""));//获奖订单
            if (dbCode != null) {
                DBOrder dbOrder = dbCode.getDbOrder();
                if (dbTimes.getWantLotteryId() != null && !dbTimes.getWantLotteryId().equals("")) {
                    DBOrder wangOrder = dbOrderService.find(Long.parseLong(dbTimes.getWantLotteryId()));
                    String[] codeInfoOne = repCode(wangOrder.getCodeInfo(), resultCode + "");
                    wangOrder.setCodeInfo(codeInfoOne[0]);
                    String codeOne = codeInfoOne[1];
                    if (dbOrder.getCodeInfo() != null && !dbOrder.getCodeInfo().equals("")) {
                        dbOrder.setCodeInfo(dbOrder.getCodeInfo().replace(resultCode + "", codeOne));
                    }
                    wangOrder.setIsLottery(Boolean.TRUE);
                    dbTimes.setDbOrder(wangOrder);
                    dbTimes.setIsLottery(Boolean.TRUE);
                    dbTimes.setReferenceCode(codeA.toString());
                    dbTimes.setReferenceCodeTwo(codeB.toString());
                    dbTimes.setLotteryMobile(wangOrder.getMobile());
                    dbTimes.setMemberId(wangOrder.getMemberId());
                    dbTimes.setTimesCode(Integer.valueOf(resultCode + ""));
                    dbOrderService.update(dbOrder); //更新中奖订单
                    dbOrderService.update(wangOrder); //更新中奖订单
                } else {
                    dbOrder.setIsLottery(Boolean.TRUE);
                    dbTimes.setDbOrder(dbOrder);
                    dbTimes.setIsLottery(Boolean.TRUE);

                    dbTimes.setReferenceCode(codeA.toString());
                    dbTimes.setReferenceCodeTwo(codeB.toString());
                    dbTimes.setLotteryMobile(dbOrder.getMobile());
                    dbTimes.setMemberId(dbOrder.getMemberId());
                    dbTimes.setTimesCode(Integer.valueOf(resultCode + ""));
                    dbOrderService.update(dbOrder); //更新中奖订单
                    if (dbOrder.getIsPrivate() == null) {
                        smsTask(dbTimes);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        update(dbTimes, "quantity", "singleAmount", "purchaseQuantity", "stepNum", "currentQuantity", "timesNo", "startDate");
        staticService.buildIndex();
        staticService.buildDBTimes(dbTimes);
        //sendTipOperatorSMS(dbTimes);
    }


    /**
     * 中奖短信提醒
     */
    public void smsTask(final DBTimes dbTimes) {
        try {
            taskExecutor.execute(new Runnable() {
                public void run() {
                    SmsUtils.sendlttSms(dbTimes.getLotteryMobile(), dbTimes.getTimesNo(), dbTimes.getDbProduct().getName(),"1730");
                    //SMSUtils.send(dbTimes.getLotteryMobile(), "http://dwz.cn/2gMpQT", "10012");
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String[] repCode(String codeInfo, String code) {
        if (codeInfo == null) {
            return null;
        }
        if (codeInfo.equals("")) {
            return null;
        }
        String[] codeList = codeInfo.split(",");
        int index = 0;
        if (codeList.length > 4) {
            index = new Random().nextInt(codeList.length);
        }
        String tCode = codeList[index];
        String[] result = new String[2];
        result[0] = codeInfo.replace(tCode, code);
        result[1] = tCode;
        return result;
    }


    /**
     * 夺宝号归还到池子
     */
    public void returnTimesPool(String timesNo, List<Integer> data) {
        Jedis jedis = jedisPool.getResource();
        for (Integer d : data) {
            jedis.lpush(timesNo, d + "");
        }
        jedisPool.returnResource(jedis);
    }


    /**
     * 生成夺宝号码池
     */
    public void createTimesPool(String timesNo, Integer size) {
        Jedis jedis = jedisPool.getResource();
        List<Integer> poolData = new ArrayList<Integer>();
        for (int i = 0; i < size; i++) {
            poolData.add(i);
        }
        Collections.shuffle(poolData);
        for (int j = 0; j < size; j++) {
            jedis.lpush(timesNo, poolData.get(j) + "");
        }
        jedisPool.returnResource(jedis);
    }


    /**
     * 从池子里面取出一个夺宝号码（lindex- key index）
     */
    public String getTimesCode(String timesNo) {
        Jedis jedis = jedisPool.getResource();
        String code = jedis.rpop(timesNo);
        jedisPool.returnResource(jedis);
        return code;
    }


    /**
     * 从池子里面取出多个夺宝码（lindex- key index）
     */
    public List<String> getTimesCodeList(String timesNo, int size) {
        List<String> codeList = new ArrayList<String>();
        try {
            Jedis jedis = jedisPool.getResource();
            for (int i = 0; i < size; i++) {
                String code = jedis.rpop(timesNo);
                if (code != null) {
                    codeList.add(code);
                }
            }
            jedisPool.returnResource(jedis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return codeList;
    }


    public Long getPoolSize(String timesNo) {
        Long count = 0L;
        try {
            Jedis jedis = jedisPool.getResource();
            count = jedis.llen(timesNo);
            jedisPool.returnResource(jedis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return count;
    }

    /**
     * 将夺宝号归还到池子
     */
    public void returnToPool(String timesNo, String[] codes) {
        try {
            Jedis jedis = jedisPool.getResource();
            for (String code : codes) {
                Long poolCode = Long.parseLong(code) - 10000000L;
                jedis.lpush(timesNo, poolCode.toString());
            }
            jedisPool.returnResource(jedis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 导出文件的最大大小 超过这个大小会压缩
     */
    private final int MAX_EXPORT_FILE_SIZE = 10 * 1024 * 1024; //10MB
    private final String storePath = "upload/excel";
    private final String EXPORT_FILENAME_PREFIX = "excel";

    @Override
    public String exportExcel2003WithUsermodel(String basePath, String contextRootPath, PageView pageView) {

        int perSheetRows = 60000; //每个sheet 6w条
        int totalRows = 0;
//        Long maxId = 0L;
        String resultURL = "";
        String fileName = generateFilename(basePath, contextRootPath, "xls");
        File file = new File(fileName);
        BufferedOutputStream out = null;
        try {
            long beginTime = System.currentTimeMillis();

            HSSFWorkbook wb = new HSSFWorkbook();
            Sheet sheet = wb.createSheet();
            Row headerRow = sheet.createRow(0);

            Cell cell = headerRow.createCell(0);
            cell.setCellValue("夺宝商品");
            cell = headerRow.createCell(1);
            cell.setCellValue("夺宝进度");
            cell = headerRow.createCell(2);
            cell.setCellValue("单份金额");
            cell = headerRow.createCell(3);
            cell.setCellValue("限购数量");
            cell = headerRow.createCell(4);
            cell.setCellValue("当前期数");
            cell = headerRow.createCell(5);
            cell.setCellValue("开始时间");
            cell = headerRow.createCell(6);
            cell.setCellValue("结束时间");
            cell = headerRow.createCell(7);
            cell.setCellValue("中奖订单");

            totalRows = 1;
            double sumFee = 0;

            pageView.setNumPerPage(200); //每次查询200行
            Long total = getMQueryCount(pageView.getFilters()); //记录总数
            int totalPage = (int) Math.ceil((double) total / (double) 200); //总页数
            int num = 0;//序号
            try {
                for (int i = 1; i <= totalPage; i++) {
                    pageView.setPageNumber(i); //当前页
                    Page<DBTimes> page = getScrollData(pageView);
                    DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                    for (DBTimes data : page.getContent()) {
                        Row row = sheet.createRow(totalRows);

                        Cell datacell = row.createCell(0);
                        datacell.setCellValue(data.getDbProduct().getName());
                        datacell = row.createCell(1);
                        datacell.setCellValue(data.getCurrentQuantity() / data.getQuantity() + "(" + data.getCurrentQuantity() + "/" + data.getQuantity() + ")");
                        datacell = row.createCell(2);
                        datacell.setCellValue(data.getSingleAmount().toString());
                        datacell = row.createCell(3);
                        datacell.setCellValue(data.getPurchaseQuantity());
                        datacell = row.createCell(4);
                        datacell.setCellValue(data.getTimesNo());
                        datacell = row.createCell(5);
                        datacell.setCellValue(format.format(data.getStartDate()));
                        String endDate = format.format(data.getEndDate());
                        if (data.getEndDate() == null) {
                            endDate = "尚未开奖";
                        }
                        datacell = row.createCell(6);
                        datacell.setCellValue(endDate);
                        datacell = row.createCell(7);
                        String DbOrder = "";
                        if (data.getDbOrder() == null) {
                            DbOrder = "尚未开奖";
                        } else {
                            DbOrder = data.getDbOrder().getMobile();
                            if (data.getIsLottery() == true) {
                                if (data.getDbOrder().getShippingDate() == null) {

                                    DbOrder = DbOrder + "等待确认地址";
                                } else {
                                    if (data.getDbOrder().getShippingStartDate() == null) {
                                        DbOrder = DbOrder + "马上发货";
                                    } else {
                                        if (data.getDbOrder().getShippingEndDate() == null) {
                                            DbOrder = DbOrder + "等待收货";
                                        } else {
                                            DbOrder = DbOrder + "已经收货";
                                        }
                                    }
                                }

                            }
                        }
                        datacell.setCellValue(DbOrder);
                        totalRows++;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            clear();
            out = new BufferedOutputStream(new FileOutputStream(file));
            wb.write(out);
            IOUtils.closeQuietly(out);
            if (needCompress(file)) {
                fileName = compressAndDeleteOriginal(fileName);
            }
            long endTime = System.currentTimeMillis();

            Map<String, Object> context = Maps.newHashMap();
            context.put("seconds", (endTime - beginTime) / 1000);
            context.put("url", fileName.replace(contextRootPath, ""));
            resultURL = fileName.replace(contextRootPath, "");
        } catch (Exception e) {
            IOUtils.closeQuietly(out);
            Map<String, Object> context = Maps.newHashMap();
            context.put("error", e.getMessage());
        }
        return resultURL;
    }

    private String compressAndDeleteOriginal(final String filename) {
        String newFileName = FilenameUtils.removeExtension(filename) + ".zip";
        compressAndDeleteOriginal(newFileName, filename);
        return newFileName;
    }

    private void compressAndDeleteOriginal(final String newFileName, final String... needCompressFilenames) {
        CompressUtils.zip(newFileName, needCompressFilenames);
        for (String needCompressFilename : needCompressFilenames) {
            FileUtils.deleteQuietly(new File(needCompressFilename));
        }
    }

    /**
     * 生成要导出的文件名
     *
     * @param basePath
     * @param contextRootPath
     * @param extension
     * @return
     */
    private String generateFilename(final String basePath, final String contextRootPath, final String extension) {
        return generateFilename(basePath, contextRootPath, null, extension);
    }

    private String generateFilename(final String basePath, final String contextRootPath, Integer index, final String extension) {
        String path = FilenameUtils.concat(contextRootPath, storePath);
        path = FilenameUtils.concat(path, basePath);
        path = FilenameUtils.concat(
                path,
                EXPORT_FILENAME_PREFIX + "_" +
                        DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") +
                        (index != null ? ("_" + index) : "") +
                        "." + extension);

        File file = new File(path);
        if (!file.exists()) {
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }
            return path;
        }
        return generateFilename(basePath, contextRootPath, extension);
    }


    private boolean needCompress(final File file) {
        return file.length() > MAX_EXPORT_FILE_SIZE;
    }


}
